package cn.edu.neu.simpleppt;

import com.jfoenix.controls.JFXSlider;
import com.jfoenix.controls.JFXTextArea;
import javafx.beans.Observable;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.input.MouseDragEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.stage.Window;

/**
 * 处理编辑界面（在点击元素时弹出的窗口） edit-info.fxml 的事件
 * 用来编辑元素属性（颜色、文本、字体大小等）
 */
public class EditInfoController {
    @FXML
    private GridPane shape_group;   // 当单击的元素不是文本时显示
    @FXML
    private GridPane text_group;    // 当单击的元素是文本是显示
    @FXML
    private VBox groups;            // shape_group 和 text_group 的容器，用来切换界面
    @FXML
    private Label name;             // 弹出窗口最上面Label
    @FXML
    private ColorPicker shapeBgColorBtn;    // 用来编辑元素的控件
    @FXML
    private ColorPicker shapeFgColorBtn;
    @FXML
    private JFXSlider shapeLineWidthSlider;
    @FXML
    private JFXSlider shapeGapsWidthSlider;
    @FXML
    private ColorPicker textColorBtn;
    @FXML
    private JFXTextArea textContentTextArea;
    @FXML
    private JFXSlider textSizeSlider;

    // 记录编辑窗口是否被移动过
    public boolean isMoved;

    // 记录上次拖拽时的鼠标位置，用来移动编辑窗口
    // 记录上次窗口的位置
    private Point2D lastMousePosition;
    private Point2D lastWindowPosition;

    // 要设置的元素
    private Node to_bind;           // 要设置的图形
    ShapeSettings settingsToAdd;    // 添加图形时使用的配置

    /**
     * 首次载入界面时调用，进行一些初始化操作
     */
    public void initialize() {
        groups.getChildren().remove(shape_group);
        groups.getChildren().remove(text_group);

        // 拖动 name Label 时移动窗口
        name.addEventFilter(MouseEvent.MOUSE_PRESSED, this::initDrag);
        name.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, this::dragging);
        name.setCursor(Cursor.MOVE);
        settingsToAdd = ShapeSettings.defaultSettings();

        // 当控件的状态发生改变时，调用对应的处理函数
        shapeBgColorBtn.valueProperty().addListener(this::onBgChanged);
        shapeFgColorBtn.valueProperty().addListener(this::onFgChanged);
        shapeLineWidthSlider.valueProperty().addListener(this::OnLineWidthChanged);
        shapeGapsWidthSlider.valueProperty().addListener(this::OnLineGapsChanged);
        textColorBtn.valueProperty().addListener(this::onTextColorChanged);
        textSizeSlider.valueProperty().addListener(this::onTextSizeChanged);
        textContentTextArea.textProperty().addListener(this::onTextContentTextChanged);

        isMoved = false;
    }

    private void onTextContentTextChanged(Observable observable, String o, String n) {
        if (to_bind == null) {
            settingsToAdd.text = n;
        } else {
            ((Label) to_bind).setText(n);
            ControlPoints controlPoints = (ControlPoints) to_bind.getUserData();
            controlPoints.updateBounds();
        }
    }

    private void onTextSizeChanged(Observable observable, Number o, Number n) {
        if (to_bind == null) {
            settingsToAdd.fontSize = n.doubleValue();
        } else {
            ((Label) to_bind).setFont(new Font(n.doubleValue()));
            ControlPoints controlPoints = (ControlPoints) to_bind.getUserData();
            controlPoints.updateBounds();
        }
    }

    private void onTextColorChanged(Observable observable, Color o, Color n) {
        if (to_bind == null) {
            settingsToAdd.fontColor = n;
        } else {
            ((Label) to_bind).setTextFill(n);
            ControlPoints controlPoints = (ControlPoints) to_bind.getUserData();
            controlPoints.updateBounds();
        }
    }

    private void OnLineWidthChanged(Observable observable, Number o, Number n) {
        if (to_bind == null) {
            settingsToAdd.lineWidth = n.doubleValue();
        } else {
            ((Shape) to_bind).setStrokeWidth(n.doubleValue());
        }
    }

    private void OnLineGapsChanged(Observable observable, Number o, Number n) {
        if (to_bind == null) {
            settingsToAdd.lineGaps = n.doubleValue();
        } else {
            Shape shape = (Shape) to_bind;
            shape.getStrokeDashArray().clear();
            if (n.doubleValue() > 0) {
                shape.getStrokeDashArray().add(n.doubleValue());
            }
        }
    }

    private void onBgChanged(Observable observable, Color o, Color n) {
        if (to_bind == null) {
            settingsToAdd.background = n;
        } else {
            ((Shape) to_bind).setFill(n);
        }
    }

    private void onFgChanged(Observable observable, Color o, Color n) {
        if (to_bind == null) {
            settingsToAdd.foreground = n;
        } else {
            ((Shape) to_bind).setStroke(n);
        }
    }

    @FXML
    protected void onSetTextAlignLeft() {
        if (to_bind == null) {
            settingsToAdd.textAlign = TextAlignment.LEFT;
        } else {
            ((Label) to_bind).setTextAlignment(TextAlignment.LEFT);
            ((Label) to_bind).setAlignment(Pos.TOP_LEFT);
        }
    }

    @FXML
    protected void onSetTextAlignCenter() {
        if (to_bind == null) {
            settingsToAdd.textAlign = TextAlignment.CENTER;
        } else {
            ((Label) to_bind).setTextAlignment(TextAlignment.CENTER);
            ((Label) to_bind).setAlignment(Pos.TOP_CENTER);
        }
    }

    @FXML
    protected void onSetTextAlignRight() {
        if (to_bind == null) {
            settingsToAdd.textAlign = TextAlignment.RIGHT;
        } else {
            ((Label) to_bind).setTextAlignment(TextAlignment.RIGHT);
            ((Label) to_bind).setAlignment(Pos.TOP_RIGHT);
            System.out.println("right");
        }
    }

    private void initDrag(MouseEvent e) {
        Window win = name.getScene().getWindow();
        lastMousePosition = new Point2D(e.getScreenX(), e.getScreenY());
        lastWindowPosition = new Point2D(win.getX(), win.getY());
        isMoved = true;
    }

    private void dragging(MouseEvent e) {
        Point2D delta = new Point2D(e.getScreenX(), e.getScreenY()).subtract(lastMousePosition);
        Window win = name.getScene().getWindow();
        win.setX(lastWindowPosition.getX() + delta.getX());
        win.setY(lastWindowPosition.getY() + delta.getY());
    }

    private void showShapeSettings() {
        groups.getChildren().remove(text_group);
        if (!groups.getChildren().contains(shape_group)) {
            groups.getChildren().add(shape_group);
        }
    }

    private void showLabelSettings() {
        groups.getChildren().remove(shape_group);
        if (!groups.getChildren().contains(text_group)) {
            groups.getChildren().add(text_group);
        }
    }

    private void updateNameLabel(PPTShapes pptShapes) {
        String new_label = null;
        switch (pptShapes) {
            case Label: new_label = "文本";       break;
            case Ellipse:
            case Circle: new_label = "椭圆";      break;
            case Line: new_label = "直线";        break;
            case PolyLine: new_label = "任意线";  break;
            case Rectangle: new_label = "矩形";   break;
        }
        name.setText(new_label);
    }

    private void updateNameLabel(Node node) {
        PPTShapes shape;
        if      (node instanceof Label) shape = PPTShapes.Label;
        else if (node instanceof Ellipse) shape = PPTShapes.Ellipse;
        else if (node instanceof Line) shape = PPTShapes.Line;
        else if (node instanceof Polyline) shape = PPTShapes.PolyLine;
        else if (node instanceof Rectangle) shape = PPTShapes.Rectangle;
        else return;
        updateNameLabel(shape);
    }

    /**
     * 当添加新元素时，读取对应的配置，并更新界面
     */
    private void loadSettings(ShapeSettings settings) {
        if (settings == null) return;
        shapeBgColorBtn.setValue(settings.background);
        shapeFgColorBtn.setValue(settings.foreground);
        shapeLineWidthSlider.setValue(settingsToAdd.lineWidth);
        shapeGapsWidthSlider.setValue(settingsToAdd.lineGaps);
        textSizeSlider.setValue(settings.fontSize);
        textColorBtn.setValue(settings.fontColor);
        textContentTextArea.setText(settings.text);
    }

    public void showSettingsToAdd(PPTShapes pptShapes) {
        to_bind = null;

        loadSettings(settingsToAdd);

        if (pptShapes == PPTShapes.Label) {
            showLabelSettings();
        } else {
            showShapeSettings();
        }
        updateNameLabel(pptShapes);
    }

    /**
     * 当元素被点击时，读取元素的属性，更新界面
     * @param source 被点击的元素
     */
    public void showSettingsToEdit(Node source) {
        to_bind = source;

        updateNameLabel(source);
        if (source instanceof Shape) {
            Shape shape = (Shape) source;
            shapeBgColorBtn.setValue((Color) shape.getFill());
            shapeFgColorBtn.setValue((Color) shape.getStroke());
            shapeLineWidthSlider.setValue(shape.getStrokeWidth());
            if (shape.getStrokeDashArray().size() == 0) {
                shapeGapsWidthSlider.setValue(0);
            } else {
                shapeGapsWidthSlider.setValue(shape.getStrokeDashArray().get(0));
            }
            showShapeSettings();
        } else if (source instanceof Label) {
            Label label = (Label) to_bind;
            textContentTextArea.setText(label.getText());
            textSizeSlider.setValue(label.getFont().getSize());
            textColorBtn.setValue((Color) label.getTextFill());
            showLabelSettings();
        }
    }

    public ShapeSettings getSettingsToAdd() {
        return settingsToAdd;
    }
}
